(function($) {
	if (typeof $ == "undefined") {
		alert("slider require jquery");
		return;
	}
	$.fn.extend({
		slider: function() {
			Math.clamp = function(num, min, max) {
				return Math.max(Math.min(num, max), min);
			};
			var Slider = window.Slider = function(_target, _realtime) {
				var target = _target.css({
					position : "relative"
				});
				var realtime = _realtime;
				var self = this;
				var DIV = "<div></div>";
				var enabled = true;
				var visible = true;
				var width = target.width() || 0;
				var height = target.height() || 0;
				var event = Slider.event;
				var style = defaultStyle();
				/*
				 * private config
				 */
				var tween = true;
				var from = 0;
				var till = 1;
				var from_last;
				var till_last;
				var posL = 0;
				var posR = width;
				var timeid;
				var background;
				/*
				 * back
				 * area left
				 * drag left
				 * clip
				 * drag right
				 * area right
				 */
				var back = $(DIV).css({
					overflow : "hidden",
					position : "absolute",
					"z-index" : 0
				}).appendTo(target);
				var areaL = $(DIV).css({
					opacity : 0.25,
					overflow : "hidden",
					position : "absolute",
					"z-index" : 1
				}).appendTo(target);
				var dragL = $(DIV).css({
					position : "absolute",
					"z-index" : 3
				}).appendTo(target);
				var clip = $(DIV).append($(DIV).css({
					width : "100%",
					height : "100%",
					background : "#ffffff",
					opacity : 0
				})).css({
					overflow : "hidden",
					position : "absolute",
					"z-index" : 2
				}).appendTo(target);
				var dragR = $(DIV).css({
					position : "absolute",
					"z-index" : 3
				}).appendTo(target);
				var areaR = $(DIV).css({
					opacity : 0.25,
					overflow : "hidden",
					position : "absolute",
					"z-index" : 1
				}).appendTo(target);
				/*
				 * init
				 */
				this.init = function() {
					unselectable(back);
					unselectable(clip);
					unselectable(dragL);
					unselectable(dragR);
					unselectable(areaL);
					unselectable(areaR);
					unselectable(target);
					var cursor_default = {
						cursor : "default"
					};
					var cursor_pointer = {
						cursor : "pointer"
					};
					var cursor_move = {
						cursor : "move"
					};
					var cursor_w_resize = {
						cursor : "w-resize"
					};
					areaL.unbind("click").css(cursor_default);
					areaR.unbind("click").css(cursor_default);
					clip.unbind("mousedown").css(cursor_default);
					dragL.unbind("mousedown").css(cursor_default);
					dragR.unbind("mousedown").css(cursor_default);
					if (enabled) {
						areaL.bind("click", clickMove).css(cursor_pointer);
						areaR.bind("click", clickMove).css(cursor_pointer);
						clip.css(cursor_move).bind("mousedown", function(e) {
							var mousedown_x = e.pageX;
							var mousedown_posL = posL;
							var mousedown_posR = posR;
							$(document).one("mouseup", function(e) {
								$(this).unbind("mousemove");
								self.updatePostion();
							}).unbind("mousemove").bind("mousemove", function(e) {
								var mousemove_x = e.pageX;
								var dx = mousemove_x - mousedown_x;
								var lt = Math.clamp(mousedown_posL + dx, 0, width - (mousedown_posR - mousedown_posL));
								var now_from = lt / width;
								var now_till = till + (now_from - from);
								from = now_from;
								till = now_till;
								self.updatePostion(realtime);
							});
						});
						dragL.css(cursor_w_resize).bind("mousedown", function(e) {
							var mousedown_x = e.pageX;
							var mousedown_posL = posL;
							$(document).one("mouseup", function(e) {
								$(this).unbind("mousemove");
								self.updatePostion();
							}).unbind("mousemove").bind("mousemove", function(e) {
								var mousemove_x = e.pageX;
								var dx = mousemove_x - mousedown_x;
								var lt = Math.clamp(mousedown_posL + dx, 0, posR);
								from = lt / width;
								self.updatePostion(realtime);
							});
						});
						dragR.css(cursor_w_resize).bind("mousedown", function(e) {
							var mousedown_x = e.pageX;
							var mousedown_posR = posR;
							$(document).one("mouseup", function(e) {
								$(this).unbind("mousemove");
								self.updatePostion();
							}).unbind("mousemove").bind("mousemove", function(e) {
								var mousemove_x = e.pageX;
								var dx = mousemove_x - mousedown_x;
								var rt = Math.clamp(mousedown_posR + dx, posL, width);
								till = rt / width;
								self.updatePostion(realtime);
							});
						});
					}
					return this;
				};
				/*
				 * event
				 */
				this.bind = function() {
					$(this).bind.apply($(this), arguments);
					return this;
				};
				this.unbind = function () {
					$(this).unbind.apply($(this), arguments);
					return this;
				};
				/*
				 * enabled
				 */
				this.setEnabled = function() {
					var _enabled = arguments.length ? arguments[0] : true;
					if (enabled != _enabled) {
						enabled = _enabled;
						this.init();
					}
					return this;
				};
				this.getEnabled = function() {
					return enabled;
				};
				/*
				 * visible
				 */
				this.setVisible = function() {
					var _visible = arguments.length ? arguments[0] : true;
					if (visible != _visible) {
						visible = _visible;
						if (target) {
							if (visible) {
								target.show();
							} else {
								target.hide();
							}
						}
					}
					return this;
				};
				this.getVisible = function() {
					return visible;
				};
				/*
				 * style
				 */
				this.setStyle = function() {
					var new_style = arguments.length ? arguments[0] : {};
					var old_style = defaultStyle();
					style = mergeStyle(old_style, new_style);
					this.updateStyle();
					return this;
				};
				this.updateStyle = function() {
					back.css(style.back).css({
						width : width,
						height : height
					});
					areaL.css(style.area).css({
						height : height
					});
					dragL.css({
						width : style.drag.width,
						height : height
					});
					clip.css(style.clip).css({
						height : height
					});
					dragR.css({
						width : style.drag.width,
						height : height
					});
					areaR.css(style.area).css({
						height : height
					});
					drawDragIcon("L");
					drawDragIcon("R");
					return this;
				};
				this.getStyle = function() {
					return style;
				};
				/*
				 * size
				 */
				this.setSize = function() {
					var w = width;
					if (arguments.length) {
						var nw = parseInt(arguments[0]);
						if (!isNaN(nw) && nw > 0) {
							w = nw;
						}
					}
					var h = height;
					if (arguments.length > 1) {
						var nh = parseInt(arguments[1]);
						if (!isNaN(nh) && nh > 0) {
							h = nh;
						}
					}
					if (w != width || h != height) {
						width = w;
						height = h;
						if (visible) {
							target.width(w).height(h);
							this.update();
							$(this).trigger(event.RESIZE, [w, h]);
						}
					}
					return this;
				};
				this.getSize = function() {
					return {
						width : width,
						height : height
					};
				};
				/*
				 * set postion
				 */
				this.setPosition = function() {
					from = arguments[0];
					from = isNaN(from) ? 0 : Math.clamp(from, 0, 1);
					till = arguments[1];
					till = isNaN(till) ? 1 : Math.clamp(till, from, 1);
					this.updatePostion();
					return this;
				};
				this.updatePostion = function() {
					if (from_last != from || till_last != till) {
						posL = Math.round(width * from);
						posR = Math.round(width * till);
						areaL.css({
							width : Math.clamp(posL, 0, width)
						});
						dragL.css({
							left : posL - style.drag.width
						});
						clip.css({
							left : posL,
							width : Math.clamp(posR - posL, 0, width)
						});
						dragR.css({
							left : posR
						});
						areaR.css({
							left : posR,
							width : Math.clamp(width - posR, 0, width)
						});
						if (arguments.length && !arguments[0]) {
							return;
						}
						from_last = from;
						till_last = till;
						$(this).trigger(event.CHANGE, [from_last, till_last]);
					}
					return this;
				};
				/*
				 * backgound
				 */
				this.setBackground = function() {
					back.empty();
					if (arguments.length) {
						background = arguments[0];
						back.append(background);
					}
					return this;
				};
				/*
				 * update
				 */
				this.update = function() {
					from_last = null;
					till_last = null;
					$(document).unbind("mousemove").unbind("mouseup");
					this.updateStyle();
					this.updatePostion();
					return this;
				};
				/*
				 * remove
				 */
				this.remove = function() {
					enabled = visible = false;
					width = height = 0;
					$(document).unbind("mousemove").unbind("mouseup");
					$(this).unbind(event.CHANGE).unbind(event.RESIZE);
					target.remove();
					return this;
				};
				this.toString = function() {
					return "[object Slider]";
				};
				/*
				 * private functions
				 */
				function defaultStyle() {
					return {
						drag : {
							width : 5,
							height : 10,
							color : "#000000"
						},
						area : {
							opacity : 0.25,
							background : "#ffffff"
						},
						clip : {
							border : "1px solid #333333",
							"margin-top" : -1,
							"margin-left" : -1
						},
						back : {
							background : "#ffffff"
						}
					};
				}

				function clickMove(e) {
					var x = getOffset(target, e).x;
					var v = x / width;
					var clip_width = posR - posL;
					var lt = Math.clamp(x - clip_width * v, 0, width - clip_width);
					var now_from = lt / width;
					var now_till = till + (now_from - from);
					if (tween) {
						var d = (now_from - from) * 0.1;
						clearInterval(timeid);
						timeid = setInterval( function() {
							from += d;
							till += d;
							if ((d > 0 && from > now_from) || (d < 0 && from < now_from)) {
								clearInterval(timeid);
								from = now_from;
								till = now_till;
								self.updatePostion();
							} else {
								self.updatePostion(false);
							}
						}, 15);
					} else {
						from = now_from;
						till = now_till;
						self.updatePostion();
					}
				}

				function drawDragIcon(type) {
					var drag = (type == "R") ? dragR : dragL;
					var pw = drag.width();
					var ph = drag.height();
					var tw = style.drag.width;
					var th = style.drag.height;
					var canvas = drag.data("canvas");
					if (!canvas) {
						canvas = getDragIcon(type, tw, th);
						drag.append(canvas).data("canvas", canvas);
					}
					$(canvas).css({
						position : "absolute",
						left : 0,
						top : (ph - th) * 0.5
					});
				}

				function getDragIcon(type, tw, th) {
					var canvas = document.createElement('canvas');
					canvas.width = tw;
					canvas.height = th;
					if (!canvas.getContext) {
						canvas = window.G_vmlCanvasManager.initElement(canvas);
					}
					setTimeout( function() {
						if (canvas.getContext) {
							var c = canvas.getContext("2d");
							if (c) {
								c.clearRect(0, 0, tw, th);
								c.fillStyle = style.drag.color;
								c.beginPath();
								var tm = th * 0.5;
								if (type == "R") {
									c.moveTo(0, tm);
									c.lineTo(tw, 0);
									c.lineTo(tw, th);
									c.lineTo(0, tm);
								} else {
									c.moveTo(tw, tm);
									c.lineTo(0, 0);
									c.lineTo(0, th);
									c.lineTo(tw, tm);
								}
								c.closePath();
								c.fill();
							}
						}
					}, 10);
					return canvas;
				}

				function mergeStyle(s1, s2) {
					var s = {};
					if (typeof s1 == "object") {
						s = s1;
					}
					if (typeof s2 == "object") {
						for (var i in s2) {
							var p = s2[i];
							if (typeof p == "object") {
								s[i] = mergeStyle(s[i], p);
							} else {
								s[i] = p;
							}
						}
					}
					return s;
				}

				function getOffset(div, e) {
					var pos = {
						x : 0,
						y : 0
					};
					var offset = $(div).offset();
					if (offset && e) {
						pos.x = e.pageX - offset.left;
						pos.y = e.pageY - offset.top;
					}
					return pos;
				}

				function unselectable(div) {
					if (div) {
						$(div).attr("unselectable", "on").css({
							"-moz-user-select" : "none",
							"-webkit-user-select" : "none"
						}).bind("selectstart", function(e) {
							return false;
						});
					}
				}

				/*
				 * return Slider
				 */
				return this.init().update();
			};
			/*
			 * static config
			 */
			Slider.event = {
				RESIZE : "resize",
				CHANGE : "change"
			};
			/*
			 * new Slider
			 */
			var realtime = arguments.length > 1 ? arguments[1] : true;
			return new Slider(this, realtime);
		}
	});
})(jQuery);